#include <thread>
#include <vector>
#include <atomic>
#include <cstddef>
#include <iostream>

// Helper print function
template <typename... Args> void print(const Args &...args)
{
    // A fold expression to print all arguments
    ((std::cout << args << " "), ...);
    std::cout << std::endl;
}

template <typename T> class UnlockQueue
{
public:
    explicit UnlockQueue(std::size_t size)
        : _ring_queue(size), _size(size), _head(0), _tail(0)
    {
    }

    // Deleted copy and move constructors/assignments
    UnlockQueue(const UnlockQueue &) = delete;
    UnlockQueue &operator=(const UnlockQueue &) = delete;

    bool enqueue(T val)
    {
        std::size_t current_tail;
        std::size_t next_tail;

        do
        {
            current_tail = _tail.load(std::memory_order_relaxed);
            next_tail = (current_tail + 1) % _size;

            if (next_tail == _head.load())
            {
                return false;
            }
            // if tail != current_tail current_tail = tail return false
            // else _tail = next_tail return true
            // _tail应该指向当前位置
        } while (!_tail.compare_exchange_weak(current_tail, next_tail,
                                              std::memory_order_release,
                                              std::memory_order_relaxed));
        _ring_queue[current_tail] = val;

        return true;
    }

    bool dequeue(T &val)
    {
        std::size_t current_head;

        do
        {
            current_head = _head.load(std::memory_order_relaxed);
            if (current_head == _tail.load(std::memory_order_acquire))
            {
                return false;
            }

            val = _ring_queue[current_head];

            // if _head != current_head current_head = _head return false
            // else _head = (current_head + 1) % _size return true
        } while (!_head.compare_exchange_weak(
            current_head, (current_head + 1) % _size, std::memory_order_release,
            std::memory_order_relaxed));

        return true;
    }

private:
    std::vector<T> _ring_queue;
    const std::size_t _size;
    // Align to cache line size to prevent false sharing
    alignas(64) std::atomic<std::size_t> _head;
    alignas(64) std::atomic<std::size_t> _tail;
};

// --- Test Code ---

// Global queue for testing
UnlockQueue<int> queue(1000);

// Atomics for verifying results
std::atomic<long long> consumed_sum = 0;
std::atomic<int> consumed_count = 0;
std::atomic<bool> producers_finished = false;

// Producer task
void produce(int items_to_produce)
{
    for (int i = 1; i <= items_to_produce; ++i)
    {
        // Spin until the item is successfully enqueued
        while (!queue.enqueue(i))
        {
            std::this_thread::yield(); // Yield to other threads if queue is
                                       // full
        }
    }
}

// Consumer task
void consume()
{
    int val;
    // Keep consuming as long as producers are working or the queue is not empty
    while (!producers_finished.load() || consumed_count.load() < 100000)
    {
        if (queue.dequeue(val))
        {
            consumed_sum += val;
            consumed_count++;
        }
        else
        {
            // If dequeue fails, yield to give producers a chance to fill the
            // queue
            std::this_thread::yield();
        }
    }
}

int main()
{
    const int num_producers = 4;
    const int num_consumers = 4;
    const int items_per_producer = 25000;
    const int total_items = num_producers * items_per_producer;

    print("--- Starting MPMC UnlockQueue Test ---");
    print("Producers:", num_producers, "| Consumers:", num_consumers);
    print("Items per producer:", items_per_producer,
          "| Total items:", total_items);

    std::vector<std::thread> producers;
    producers.reserve(num_producers);
    std::vector<std::thread> consumers;
    consumers.reserve(num_consumers);

    // Start consumers
    for (int i = 0; i < num_consumers; ++i)
    {
        consumers.emplace_back(consume);
    }

    // Start producers
    for (int i = 0; i < num_producers; ++i)
    {
        producers.emplace_back(produce, items_per_producer);
    }

    // Wait for all producers to finish
    for (auto &p : producers)
    {
        p.join();
    }

    // Signal consumers that producers are done
    producers_finished.store(true);
    print("\n--- Producers finished ---");

    // Wait for all consumers to finish
    for (auto &c : consumers)
    {
        c.join();
    }
    print("--- Consumers finished ---");

    // --- Verification ---
    print("\n--- Verification ---");
    // The sum of an arithmetic sequence from 1 to N is N * (N + 1) / 2
    long long expected_sum =
        (long long)items_per_producer * (items_per_producer + 1) / 2;
    expected_sum *=
        num_producers; // Since each producer produces the same sequence

    print("Total items produced:", total_items);
    print("Total items consumed:", consumed_count.load());
    print("Expected sum:", expected_sum);
    print("Consumed sum:", consumed_sum.load());

    if (consumed_count.load() == total_items &&
        consumed_sum.load() == expected_sum)
    {
        print("\n[SUCCESS] The UnlockQueue passed the concurrency test!");
    }
    else
    {
        print("\n[FAILURE] The UnlockQueue failed the test. Data was lost or "
              "corrupted.");
    }

    return 0;
}
// int main()
// {
//     alignas(64) int a = 10;
//     print("sizeof(a) = ", sizeof a);
//     return 0;
// }